#!/usr/bin/env python
# -*- coding: utf-8 -*-

import sys
import re
import os.path
import fnmatch
import email
import quopri
#import urllib
import urlparse
import HTMLParser
from BeautifulSoup import BeautifulSoup
import inspect
import pickle
import optparse
import cmd
import subprocess
import codecs
import tempfile

version = 4.0

def dbg_out(obj) :
    print "%s:%d" % inspect.stack()[1][1:3], obj

def is_host(addr) :
    if addr.endswith('/32') :
        return True
    else :
        return False

def default_lifetime(protocol) :
    if protocol == 'TCP' :
        return 1800
    elif protocol == 'UDP' :
        return 60
    elif protocol == 'ICMP' :
        return 10
    else :
        return 30

def escape_space(string) :
    return string.replace(" ", "|").encode('utf-8')

def escape_special_for_name(string) :
    t = string.replace(' ', '_')
    t = t.replace(',', '_')
    return t

N_HASH_MARK = 100 / 50
def update_progress(header, total, current) :
    last_progress = current * 100 / total
    progress = (current + 1) * 100 / total
    if last_progress != progress :
        sys.stdout.write('\r{0} [{1}] {2}% {3}/{4}'.format
                         (header, '#'*(progress/N_HASH_MARK), progress, current, total))
        sys.stdout.flush()

class Config :
    def __init__(self) :
        self.service_objects = []
        self.route_table = None
        self.network_interfaces = None
        self.network_objects = None
        self.rules = None
        self.nat_rules = None
        self.version = 4.0
        self.out_file = None

    def register_mf2_config(self, mf2_config) :
        self.mf2_config = mf2_config

    def write(self, string) :
        if self.out_file == None :
            print >> sys.stderr, "Cannot write before set output file"
            return
        self.out_file.write(('%s\n' % string).encode('utf-8'))

    def set_output_file(self, filename) :
        if self.out_file != None :
            self.out_file.close()
        self.out_file = file(filename, 'w')

    def close_output_file(self) :
        if self.out_file != None :
            self.out_file.close()
        self.out_file = None

    def verify_rule(self) :
        for rule in self.rules :
            for src_obj in rule.source :
                found = False
                for net_obj in self.network_objects :
                    if net_obj.name == src_obj :
                        found = True
                        break
                if found == False :
                    print ("Unknown object for source : %s in Rule %s" 
                           % (src_obj, rule.sequence))
            for dst_obj in rule.destination :
                found = False
                for net_obj in self.network_objects :
                    if net_obj.name == dst_obj :
                        found = True
                        break
                if found == False :
                    print ("Unknown object for destination : %s in Rule %s" 
                           % (dst_obj, rule.sequence))
            for svc_obj_in_rule in rule.service :
                found = False
                for svc_obj in self.service_objects :
                    if svc_obj.name == svc_obj_in_rule :
                        found = True
                        break
                if found == False :
                    print ("Unknown service object : %s in Rule %s"
                           % (svc_obj_in_rule, rule.sequence))

    def search_network_object_with_address(self, target_addr) :
        for net_obj in self.network_objects :
            for addr in net_obj.addr :
                if target_addr == addr :
                    return net_obj
        return None

    def process_host_object(self, name, zone, addr, description) :
        if len(addr) == 1 :
            host = MF2_Host_Object(name, zone, addr[0][:-3], description)
            self.mf2_config.add_host(host)
        else:
            print("Host Object %s has more than 1 IP address." % name)
            sys.exit(1)

    def process_network_object(self, name, zone, addrs, description) :
        member = []
        for entry in addrs :
            if is_host(entry) :
                entry = entry[:-3]
                found = self.mf2_config.lookup_host_object_with_addr_zone(entry, zone)
                if found != None :
                    member.append("%sH %s" % (found.zone[0].upper(), found.name))
                else :
                    duplicate = self.mf2_config.lookup_host_object_with_addr(entry)
                    if duplicate != None :
                        print "conflict zone : %s: %s(%s) as member of %s(%s)" % (entry, 
                                                                                  duplicate.name, duplicate.zone,
                                                                                  name, zone)
                    host_name = "%s_%s" %(entry, zone[0].upper())
                    member_description = "Appended as member of %s" % name
                    host = MF2_Host_Object(host_name, zone, entry, member_description)
                    self.mf2_config.add_host(host)
                    member.append("%sH %s" % (zone[0].upper(), host_name))
            else:
                found = self.mf2_config.lookup_network_object_with_addr_zone(entry, zone)
                if found != None :
                    member.append("%sN %s" % (found.zone[0].upper(), found.name))
                else:
                    duplicate = self.mf2_config.lookup_network_object_with_addr(entry)
                    if duplicate != None :
                        print "conflict zone : %s: %s(%s) as member of %s(%s)" % (entry, 
                                                                                  duplicate.name, duplicate.zone,
                                                                                  name, zone)
                    #print "entry = %s" % entry
                    network_name = "%s_%s" % (entry, zone[0].upper())
                    #print "network_name = %s" % network_name
                    member_description = "Appended as member of %s" % name
                    network = MF2_Network_Object(network_name, zone,
                                                 entry, member_description)
                    self.mf2_config.add_network(network)
                    member.append("%sN %s" % (zone[0].upper(), network_name))

        group = MF2_Group_Object(name, zone, member, description)
        self.mf2_config.add_group(group)

    def process_group_object(self, name, zone, member_list, description) :
        member = []
        for entry in member_list :
            if is_host(entry) :
                entry = entry[:-3]
                found = self.mf2_config.lookup_host_object_with_addr_zone(entry, zone)
                if found != None :
                    member.append("%sH %s" % (found.zone[0].upper(), found.name))
                else:
                    duplicate = self.mf2_config.lookup_host_object_with_addr(entry)
                    if duplicate != None :
                        print "conflict zone : %s: %s(%s) as member of %s(%s)" % (entry, 
                                                                                  duplicate.name, duplicate.zone,
                                                                                  name, zone)
                    host_name = "%s_%s" % (entry, zone[0].upper())
                    member_description = "Appended as member of %s" % name
                    #print "host_name = %s" % host_name
                    #print "zone = %s" % zone
                    host = MF2_Host_Object(host_name, zone, entry, member_description)
                    self.mf2_config.add_host(host)
                    member.append("%sH %s" % (zone[0].upper(), host_name))
                    
            else :
                found = self.mf2_config.lookup_network_object_with_addr_zone(entry, zone)
                if found != None:
                    member.append("%sN %s" % (found.zone[0].upper(), found.name))
                else :
                    duplicate = self.mf2_config.lookup_network_object_with_addr(entry)
                    if duplicate != None :
                        print "conflict zone : %s: %s(%s) as member of %s(%s)" % (entry, 
                                                                                  duplicate.name, duplicate.zone,
                                                                                  name, zone)
                    network_name = "%s_%s" % (entry, zone[0].upper())
                    member_description = "Appended as member of %s" % name
                    #print "network_name = %s" % network_name
                    #print "zone = %s" % zone
                    network = MF2_Network_Object(network_name, zone,
                                                 entry, member_description)
                    self.mf2_config.add_network(network)
                    member.append("%sN %s" % (zone[0].upper(), network_name))

        #print "member = %s" % member
        group = MF2_Group_Object(name, zone, member, description)
        self.mf2_config.add_group(group)

    def translate_network_object(self) :
        oulim_special_objects = {
            'IF-ALL' : '시스템 전체 인터페이스 주소',
            'NET-ALL' : '전체 인터넷',
            'IF-INT' : '시스템 내부 인터페이스 주소',
            'IF-EXT' : '시스템 외부 인터페이스 주소',
            'IF-SVC' : '시스템 서비스존 인터페이스 주소',
            'HOST-LOCAL' : '시스템 로컬 루프백(loopback) 주소',
            'IF-eth' : '시스템 로컬 네트워크 인터페이스',
            }
        # Only for Host 
        for net_obj in self.network_objects[1:] :
            if net_obj.type == 'Host' :
                self.process_host_object(net_obj.name, net_obj.zone, 
                                         net_obj.addr,
                                         net_obj.description)
            elif net_obj.type == 'Network' or net_obj.type == 'Host Group' :
                pass
            else :
                if (net_obj.name not in oulim_special_objects 
                    and net_obj.name[:6] not in oulim_special_objects) :
                    print("Unknown Object Type : %s in %s" 
                          % (net_obj.type, net_obj.name))
        # Then network and host group
        for net_obj in self.network_objects[1:] :
            if net_obj.type == 'Host' :
                pass
            elif net_obj.type == 'Network' :
                self.process_network_object(net_obj.name, net_obj.zone, 
                                            net_obj.addr, net_obj.description) 
            elif net_obj.type == 'Host Group' :
                self.process_group_object(net_obj.name, net_obj.zone,
                                          net_obj.addr, net_obj.description)
            else :
                if (net_obj.name not in oulim_special_objects 
                    and net_obj.name[:6] not in oulim_special_objects) :
                    print("Unknown Object Type : %s in %s" 
                          % (net_obj.type, net_obj.name))
            
                    #sys.exit(1)

    def classify_group_from_service(self) :
        svc_obj_list = []
        svc_grp_obj_list = []
        return svc_obj_list, svc_grp_obj_list

    def translate_service_object(self) :
        # temporary list
        service_object_list, service_group_object_list = self.classify_group_from_service()
        
        KNOWN_PROTOCOL = set([ "AH", "EIGRP", "ESP", "GRE", "IGMP", "IGP", "IPComp", "IPv4",
                               "ISIS", "L2TP", "OSPF", "PIM", "RSVP", "SKIP" ])

        # 서비스 오브젝트를 만들 것인지 서비스 그룹 오브젝트를 만들 것인지 결정
        for svc_obj in self.service_objects :
            object_type = 'homogenious'
            for seq, entry in enumerate(svc_obj.port) :
                protocol_port_list = entry.split('/')
                if len(protocol_port_list) == 1 :
                    print "Ignore Service Entry: %s" % entry
                    object_type = 'special'
                elif len(protocol_port_list) == 2 :
                    print "Register Protocol Entry: %s" % entry
                    if protocol_port_list[0] not in KNOWN_PROTOCOL :
                        print "Unknown protocol : %s" % protocol_port_list[0]
                    else :
                        if len(svc_obj.port) != 1 :
                            object_type = 'heterogenious'
                elif len(protocol_port_list) == 3 :
                    if protocol_port_list[1] == 'ALL' :
                        if len(svc_obj.port) != 1 :
                            object_type = 'heterogenious'
                    elif protocol_port_list[0] == 'ICMP' :
                        if protocol_port_list[1] == 'ECHO' :
                            if len(svc_obj.port) != 1 :
                                object_type = 'heterogenious'
                    else :
                        print "Handle strange proto/dir/port/ notation: %s" % entry
                        object_type = 'special'
                else:
                    # check the homogenity of protocol and direction
                    if seq == 0 :
                        first_protocol = protocol_port_list[0]
                        first_direction = protocol_port_list[1]
                    else :
                        if (first_protocol != protocol_port_list[0]
                            or first_direction != protocol_port_list[1]) :
                            object_type = 'heterogenious'
            if object_type == 'homogenious' and len(svc_obj.port) < 10 :
                # service object
                service_object_list.append(svc_obj)
            elif object_type == 'heterogenious' or len(svc_obj.port) >= 10 :
                # service group
                service_group_object_list.append(svc_obj)
            else :
                if len(svc_obj.port) != 1 :
                    print "Contradiction"
                    sys.exit(1)

        # register all service object
        for svc_obj in service_object_list :
            port_list = []
            direction = 'UNKNOWN'
            for entry in svc_obj.port :
                protocol_port_list = entry.split('/')
                protocol = protocol_port_list[0]
                if len(protocol_port_list) == 2 :
                    # Do not register protocol object : use default one
                    service = MF2_Service_Object(svc_obj.name, protocol, "NONE",
                                                 "*", "*", default_lifetime(protocol),
                                                 svc_obj.description)
                    self.mf2_config.add_service(service)
                elif len(protocol_port_list) == 3 :
                    # Do not use 'Protocol name' / ALL object
                    service = MF2_Service_Object(svc_obj.name, protocol, "NONE",
                                                 "*", "*", default_lifetime(protocol),
                                                 svc_obj.description)
                    self.mf2_config.add_service(service)
                elif len(protocol_port_list) == 4 :
                    direction = protocol_port_list[1]
                    port = protocol_port_list[2]
                    if port[0] == '=' :
                        if port[1:] == '0' :
                            print "Invalid Port Number: 0 in %s" % svc_obj.name
                        else :
                            port_list.append(port[1:])
                    elif port.count('-') != 0 :
                        port_list.append(port)
                    elif port.startswith("&gt;=") :
                        print "%s-65535" % port[5:]
                        port_list.append("%s-65535" % port[5:])
                    elif port.startswith("&lt;=") :
                        print "1-%s"% port[5:]
                        port_list.append("1-%s"% port[5:])
                    else :
                        print "Unknown Form %s in %s" % (entry, svc_obj.name)
            if len(port_list) != 0 :
                if direction == 'DST' :
                    protocol = protocol_port_list[0]
                    service = MF2_Service_Object(svc_obj.name, protocol, "NONE",
                                                 '*', ','.join(port_list), 
                                                 default_lifetime(protocol),
                                                 svc_obj.description)
                    self.mf2_config.add_service(service)
                elif direction == 'SRC' :
                    service = MF2_Service_Object(svc_obj.name, protocol, "NONE",
                                                 ','.join(port_list), '*',
                                                 default_lifetime(protocol),
                                                 svc_obj.description)
                    self.mf2_config.add_service(service)
                else :
                    print "Unknown Direction %s in %s" % (direction, svc_obj.name)

        # register all service group object
        for svc_grp_obj in service_group_object_list :
            member = []
            for entry in svc_grp_obj.port :
                protocol_port_list = entry.split('/')
                if len(protocol_port_list) == 2 :
                    protocol = protocol_port_list[0]
                    direction = "NONE"
                elif len(protocol_port_list) == 3 :
                    protocol = protocol_port_list[0]
                    direction = "ALL"
                elif len(protocol_port_list) == 4 :
                    protocol = protocol_port_list[0]
                    direction = protocol_port_list[1]
                    port = protocol_port_list[2]
                    if port[0] == '=' :
                        if port[1:] == '0' :
                            print "Invalid Port Number: 0 in %s" % svc_grp_obj.name
                            continue
                        else :
                            port = port[1:]
                    elif port.startswith("&gt;=") :
                        port = "%s-65535" % port[5:]
                    elif port.startswith("&gt;") :
                        port = "%d-65535" % (int(port[5:]) + 1)
                    elif port.startswith("&lt;=") :
                        port = "1-%s" %port[5:]
                    elif port.startswith("&lt;") :
                        port = "1-%d" % (int(port[5:]) - 1)
                else :
                    print "Error: %s" % entry
                    sys.exit(2)
                found = self.mf2_config.lookup_service_object_with_port(protocol, direction, port)
                if found != None :
                    member.append("%s" % found.name)
                else :
                    service_name = "%s_%s" % (protocol, port)
                    service_description = "added as member of %s" % svc_grp_obj.name
                    if direction == 'DST' :
                        service = MF2_Service_Object(service_name, protocol, "NONE",
                                                     "*", port, default_lifetime(protocol),
                                                     service_description)
                    elif direction == 'SRC' :
                        service = MF2_Service_Object(service_name, protocol, "NONE",
                                                     port, "*", default_lifetime(protocol),
                                                     service_description)
                    else :
                        print "Unknown Direction %s in %s" % (direction, svc_grp_obj.name)
                        continue
                    self.mf2_config.add_service(service)
                    member.append("%s" % service_name)

            # try find sub service line by line
            # if not, add new sub service
            service_group = MF2_Service_Group_Object(svc_grp_obj.name, member, svc_grp_obj.description)
            self.mf2_config.add_service_group(service_group)

    def translate_network_list(self, oulim_list, rule) :
        mf2_list = []
        for entry in oulim_list :
            host_obj = self.mf2_config.lookup_host_object(entry)
            if host_obj != None :
                mf2_list.append("%sH %s" % (host_obj.zone[0].upper(), host_obj.name))
                continue
            network_obj = self.mf2_config.lookup_network_object(entry)
            if network_obj != None :
                mf2_list.append("%sN %s" % (netowrk_obj.zone[0].upper(), network_obj.name))
                continue
            group_obj = self.mf2_config.lookup_group_object(entry)
            if group_obj != None :
                mf2_list.append("%sG %s" % (group_obj.zone[0].upper(), group_obj.name))
                continue
            if entry == 'NET-ALL' :
                mf2_list.append("AA ANY")
                continue
            print "Not Found Object : %s from %s" % (entry.encode('utf-8'), rule.sequence)
            
            #sys.exit(3)
        return mf2_list

    def translate_service_list(self, oulim_list, rule) :
        mf2_list = []
        for entry in oulim_list :
             svc_obj = self.mf2_config.lookup_service_object(entry)
             if svc_obj != None :
                 if entry == 'ALL' :
                     print "Found ALL in service list"
                     sys.exit(3)
                 mf2_list.append("SS %s" % svc_obj.name)
                 continue
             svc_grp_obj = self.mf2_config.lookup_service_group_object(entry)
             if svc_grp_obj != None :
                 if entry == 'ALL' :
                     print "Found ALL in service group list"
                     sys.exit(3)
                 mf2_list.append("SG %s" % svc_grp_obj.name)
                 continue
             #print rule
             print ("Not Found: %s in %s" % (entry, rule.sequence)).encode('utf-8')
             #sys.exit(3)
        if len(mf2_list) == 0 :
            mf2_list.append("SA ALL")
        return mf2_list
            
    def translate_fw4_rule(self) :
        for rule in self.rules :
            src_list = self.translate_network_list(rule.source, rule)
            dst_list = self.translate_network_list(rule.destination, rule)
            svc_list = self.translate_service_list(rule.service, rule)
            mf2_rule = MF2_FW4_Rule(rule.status == 'Enable', 
                                    src_list, 
                                    dst_list,
                                    svc_list, 
                                    rule.action, 
                                    rule.log == 'Enable', 
                                    rule.description)
            self.mf2_config.add_fw4_rule(mf2_rule)
            if rule.direction == 'BI' :
                mf2_rule = MF2_FW4_Rule(rule.status == 'Enable',
                                        dst_list,
                                        src_list,
                                        svc_list,
                                        rule.action,
                                        rule.log == 'Enable',
                                        rule.description)
                self.mf2_config.add_fw4_rule(mf2_rule)

    def process_route(self, route_list) :
        route_table = []
        route_table_columns = []
        if version == 2.0 :
            t = route_list[0]
        elif version == 3.0 :
            t = route_list[1]
        else :
            t = route_list[1]
        for col in t :
            if col.__str__().strip() != '':
                route_table_columns.append(col.b.contents[0])
        route = Route()
        route.process_columns(route_table_columns)
        route_table.append(route)        
        for route_entry in route_list[2:] :
            route_table_columns = []
            for col in route_entry :
                if col.__str__().strip() != '':
                    route_table_columns.append(h.unescape(
                            col.contents[0]).strip())
            route = Route()
            route.process_columns(route_table_columns)
            route_table.append(route)

        self.route_table = route_table

    def process_network(self, network_list) :
        network_columns = []
        for col in network_list[0] :
            if col.__str__().strip() != '':
                if version == 2.0 :
                    t = col.font.contents[0].strip()
                else :
                    t = col.contents[0].strip()
                network_columns.append(t)

        interface_list = []

        interface = Interface()
        if version == 2.0 :
            interface.set_name(network_columns[0])
            interface.set_addr(network_columns[1])
            interface.set_hw_addr(network_columns[2])
            interface.set_mtu(network_columns[3])
            interface.set_status(network_columns[4])
            interface.set_zone(network_columns[5])
            range_end = -2
        else :
            interface.set_name(network_columns[0])
            interface.set_description(network_columns[1])
            interface.set_zone(network_columns[2])
            interface.set_level(network_columns[3])
            interface.set_type(network_columns[4])
            interface.set_addr(network_columns[5])
            range_end = None
        # interface.show()
        interface_list.append(interface) # append first column

        for network in network_list[2:range_end] :
            #print network[0]
            network_columns = []
            for col in network :
                if col.__str__().strip() != '':
                    network_columns.append(col)
            interface = Interface()
            if version == 2.0 :
                process_network_name(interface, network_columns[0])
                process_network_address(interface, network_columns[1])
                process_network_hw_addr(interface, network_columns[2])
                process_network_mtu(interface, network_columns[3])
                process_network_status(interface, network_columns[4])
                process_network_zone(interface, network_columns[5])
            else :
                process_network_name(interface, network_columns[0])
                process_network_description(interface, network_columns[1])
                process_network_zone(interface, network_columns[2])
                process_network_level(interface, network_columns[3])
                process_network_type(interface, network_columns[4])
                process_network_address(interface, network_columns[5])

            # interface.show()
            interface_list.append(interface)

        self.network_interfaces = interface_list

    def process_network_group(self, ng_list) :
        network_group_columns = []
        # Process header
        if version == 2.0 :
            for col in ng_list[0] :
                if col.__str__().strip() != '':
                    t = col.font.contents[0].strip()
                    network_group_columns.append(t)

            network_object = NetworkObject()
            network_object.set_name(escape_special_for_name(network_group_columns[0]))
            network_object.set_description(network_group_columns[1])
            network_object.set_level(network_group_columns[2])
            network_object.set_type(network_group_columns[3])
            network_object.set_addr(network_group_columns[4])
            network_object.set_etc(network_group_columns[5])
            
            network_object_list = []
            network_object_list.append(network_object)

            for network_group in ng_list[1:-1] :
                network_group_columns = []
                for col in network_group :
                    if col.__str__().strip() != '' :
                        network_group_columns.append(col)
                network_object = NetworkObject()
                process_network_group_name(network_object, 
                                           network_group_columns[0])
                process_network_group_description(network_object,
                                                  network_group_columns[1])
                process_network_group_level(network_object, 
                                            network_group_columns[2])
                process_network_group_type(network_object, 
                                           network_group_columns[3])
                process_network_group_addr(network_object, 
                                           network_group_columns[4])
                process_network_group_etc(network_object, 
                                          network_group_columns[5])
                network_object_list.append(network_object)
                
            self.network_objects = network_object_list
        else :
            for col in ng_list[0] :
                if col.__str__().strip() != '' :
                    network_group_columns.append(col.contents[0].strip())

            network_object = NetworkObject()
            network_object.set_name(network_group_columns[0])
            network_object.set_description(network_group_columns[1])
            network_object.set_zone(network_group_columns[2])
            network_object.set_level(network_group_columns[3])
            network_object.set_type(network_group_columns[4])
            network_object.set_addr(network_group_columns[5])
            network_object.set_etc(network_group_columns[6])
            
            network_object_list = []
            network_object_list.append(network_object)
            
            for network_group in ng_list[1:] :
                network_group_columns = []
                for col in network_group :
                    if col.__str__().strip() != '' :
                        network_group_columns.append(col)
                network_object = NetworkObject()
                process_network_group_name(network_object,
                                           network_group_columns[0])
                process_network_group_description(network_object,
                                                  network_group_columns[1])
                process_network_group_zone(network_object,
                                           network_group_columns[2])
                process_network_group_level(network_object,
                                            network_group_columns[3])
                process_network_group_type(network_object,
                                           network_group_columns[4])
                process_network_group_addr(network_object,
                                           network_group_columns[5])
                process_network_group_etc(network_object,
                                          network_group_columns[6])
                network_object_list.append(network_object)
            self.network_objects = network_object_list

    def process_service(self, service_list, protocol = 'None') :
        service_object_list = self.service_objects
        for service in service_list[1:] :
            col_list = []
            for col in service :
                if col.__str__().strip() != '':
                    col_list.append(col)
            #print len(col_list)
            if version == 2.0 :
                service = Service(protocol)
                process_service_name(service, col_list[0])
                process_service_port(service, col_list[1])
                process_service_description(service, col_list[2])
                service_object_list.append(service)
                if len(col_list) == 8 :
                    service = Service(protocol)
                    process_service_name(service, col_list[4])
                    process_service_port(service, col_list[5])
                    process_service_description(service, col_list[6])
                    service_object_list.append(service)
            else :
                service = Service(protocol)
                process_service_name(service, col_list[0])
                process_service_description(service, col_list[1])
                process_service_port(service, col_list[2])
                service_object_list.append(service)

    def process_rule(self, rule_list) :
        rule_entry_list = []
        rule = rule_list[0]
        if version == 2.0 :
        #for rule in rule_list :
        #print "Number of rule = ", len(rule_list) - 3
            # parsing Header
        #for col in rule :
        #    if col.__str__().strip() != '':
        #        print col.b.font.contents[0].strip().encode('utf-8')
            for rule in rule_list[1:-2] :
                columns = []
                for col in rule :
                    if col.__str__().strip() != '':
                        columns.append(col)
                converted = process_rule_entry(columns)
                rule_entry_list.append(converted)
                # converted.show()
        elif version == 3.0 :
            for rule in rule_list[2:] :
                columns = []
                for col in rule :
                    if col.__str__().strip() != '':
                        columns.append(col)
                if len(columns) == 12 :
                    converted = process_rule_entry(columns)
                    rule_entry_list.append(converted)
                elif len(columns) == 1 :
                    process_rule_description(converted, columns[0])
                    # converted.show()
        elif version == 4.0 :
            for rule in rule_list[2:] :
                columns = []
                for col in rule :
                    if col.__str__().strip() != '':
                        columns.append(col)
                if len(columns) == 15 :
                    converted = process_rule_entry(columns)
                    rule_entry_list.append(converted)
                elif len(columns) == 1 :
                    process_rule_description(converted, columns[0])
        else :
            pass
        self.rules = rule_entry_list

        # discard two blocks : 
        #	One for <br /> 
        #	Another for 3 buttons : (Add Rule) (Apply) (Current List)

    def process_nat(self, nat_list) :
        #for col in nat_list[0]:
        #    if col.__str__().strip() != '' :
        #        print col.b.font.contents[0].strip().encode('utf-8')
        nat_rule_list = []
        if version == 2.0 :
            for nat in nat_list[1:] :
                col_list = []
                for col in nat :
                    if col.__str__().strip() != '' :
                        col_list.append(col)

                # print "Number of columns in NAT rule = %d" % len(col_list)
                nat_rule = NAT_RULE()
                process_nat_sequence(nat_rule, col_list[0])
                process_nat_type(nat_rule, col_list[1])
                process_nat_nic(nat_rule, col_list[2])
                process_nat_private_addr(nat_rule, col_list[3])
                process_nat_public_addr(nat_rule, col_list[4])
                process_nat_protocol(nat_rule, col_list[5])
                process_nat_port(nat_rule, col_list[6])
                process_nat_status(nat_rule, col_list[7])
                process_nat_etc(nat_rule, col_list[8])
                process_nat_description(nat_rule, col_list[9])
                nat_rule_list.append(nat_rule)
        elif version == 3.0 :
            for nat in nat_list[2:] :
                col_list = []
                for col in nat :
                    if col.__str__().strip() != '' :
                        col_list.append(col)
                if len(col_list) == 10 :
                    nat_rule = NAT_RULE()
                    process_nat_sequence(nat_rule, col_list[0])
                    process_nat_type(nat_rule, col_list[1])
                    process_nat_nic(nat_rule, col_list[2])
                    process_nat_private_addr(nat_rule, col_list[3])
                    process_nat_public_addr(nat_rule, col_list[4])
                    process_nat_protocol(nat_rule, col_list[5])
                    process_nat_port(nat_rule, col_list[6])
                    process_nat_status(nat_rule, col_list[8])
                    nat_rule_list.append(nat_rule)
                else :
                    process_nat_description(nat_rule, col_list[0])
        elif version == 4.0 :
            for nat in nat_list[2:] :
                col_list = []
                for col in nat :
                    if col.__str__().strip() != '' :
                        col_list.append(col)
                nat_rule = NAT_RULE()
                process_nat_sequence(nat_rule, col_list[0])
                process_nat_type(nat_rule, col_list[1])
                process_nat_nic(nat_rule, col_list[2])
                process_nat_private_addr(nat_rule, col_list[3])
                process_nat_public_addr(nat_rule, col_list[4])
                process_nat_protocol(nat_rule, col_list[5])
                process_nat_port(nat_rule, col_list[6])
                process_nat_status(nat_rule, col_list[7])
                process_nat_etc(nat_rule, col_list[8])
                # process_nat_description(nat_rule, col_list[9])
                nat_rule_list.append(nat_rule)
        self.nat_rules = nat_rule_list

    def show(self) :
        if self.route_table != None and len(self.route_table) != 0 :
            print
            print "Route Table"
            print
            for entry in self.route_table :
                print entry
        if (self.network_interfaces != None 
            and len(self.network_interfaces) != 0) :
            print
            print "Network Interface / Zone"
            print
            for entry in self.network_interfaces :
                print entry
        if (self.network_objects != None 
            and len(self.network_objects) != 0) :
            print
            print "Network Objects"
            print
            for entry in self.network_objects :
                print entry
        if len(self.service_objects) != 0 :
            print
            print "Service Objects"
            print
            for entry in self.service_objects :
                print entry
        if self.rules != None and len(self.rules) != 0 :
            print
            print "Rules"
            print
            for entry in self.rules :
                print entry
        if self.nat_rules != None and len(self.nat_rules) != 0:
            print
            print "NAT Rules"
            print
            for entry in self.nat_rules :
                print entry

class Rule :
    def __init__(self) :
        self.action = ""
        self.direction = ""
        self.sequence = ""
        self.nic = ""
        self.source = ""
        self.destination = ""
        self.service = ""
        self.status = ""
        self.log = ""
        self.description = ""
    def __repr__(self) :
        return ("%s:%s:%s:%s:%s:%s:%s:%s:%s:%s" % (
                self.action, self.direction, self.sequence, self.nic,
                ', '.join(self.source), ', '.join(self.destination),
                ', '.join(self.service), self.status, 
                self.log, self.description)).encode('utf-8')
    def show(self) :
        print ("%s:%s:%s:%s:%s:%s:%s:%s" % (
                self.action, self.sequence, self.nic,
                ', '.join(self.source), ', '.join(self.destination), ', '.join(self.service),
                self.status, self.log)).encode('utf-8')
    def set_direction(self, direction) :
        self.direction = direction
    def set_action(self, action) :
        self.action = action
    def set_sequence(self, sequence) :
        self.sequence = sequence
    def set_nic(self, nic) :
        self.nic = nic
    def set_source(self, source) :
        self.source = source
    def set_destination(self, destination) :
        self.destination = destination
    def set_service(self, service) :
        self.service = service
    def set_status(self, status) :
        self.status = status
    def set_log(self, log) :
        self.log = log
    def set_description(self, description) :
        self.description = description

class Interface :
    def __init__(self) :
        self.name = ""
        self.addr = ""
        self.hw_addr = ""
        self.mtu = ""
        self.status = ""
        self.zone = ""
        self.description = ""
        self.level = ""
        self.type = ""
    def show(self) :
        if version == 2.0 :
            print "%s:%s:%s:%s:%s:%s" % (self.name, self.addr, 
                                         self.hw_addr, self.mtu,
                                         self.status, self.zone)
        else :
            print "%s:%s:%s:%s:%s:%s" % (
                    self.name, self.description, self.zone,
                    self.level, self.type, self.addr)
    def __repr__(self) :
        if version == 2.0 :
            return ("%s:%s:%s:%s:%s:%s" % (
                    self.name, self.addr, self.hw_addr, self.mtu,
                    self.status, self.zone)).encode('utf-8')
        else :
            return ("%s:%s:%s:%s:%s:%s" % (
                    self.name, self.description, self.zone,
                    self.level, self.type, self.addr)).encode('utf-8')
    def set_name(self, name) :
        self.name = name
    def set_addr(self, addr) :
        self.addr = addr
    def set_hw_addr(self, hw_addr) :
        self.hw_addr = hw_addr
    def set_mtu(self, mtu) :
        self.mtu = mtu
    def set_status(self, status) :
        self.status = status
    def set_zone(self, zone) :
        self.zone = zone
    def set_description(self, description) :
        self.description = description
    def set_level(self, level) :
        self.level = level
    def set_type(self, type) :
        self.type = type

class NetworkObject :
    def __init__(self) :
        self.name = ""
        self.addr = ""
        self.level = ""
        self.description = ""
        self.type = ""
        self.etc = ""
        self.zone = ""
    def show(self) :
        print "%s:%s:%s:%s:%s:%s" % (
            self.name, self.addr, self.level, self.description, 
            self.type, self.etc)
    def __repr__(self) :
        if version == 2.0 :
            return ("%s:%s:%s:%s:%s:%s" % (
                    self.name, self.addr, self.level, self.description, 
                    self.type, self.etc)).encode('utf-8')
        else :
            # return ("%s:%s:%s:%s:%s:%s" % (
            #         type(self.name), type(self.addr), type(self.level), 
            #         type(self.zone), type(self.type),
            #         type(self.description))).encode('utf-8')
            return ("%s:%s:%s:%s:%s:%s" % (
                    self.name, self.addr, self.level, self.zone, self.type,
                    self.description)).encode('utf-8')
    def set_name(self, name) :
        self.name = name
    def set_zone(self, zone) :
        self.zone = zone
    def set_addr(self, addr) :
        self.addr = addr
    def set_level(self, level) :
        self.level = level
    def set_description(self, description) :
        self.description = description
    def set_type(self, object_type) :
        self.type = object_type
    def set_etc(self, etc) :
        self.etc = etc

class Service :
    def __init__(self, protocol) :
        self.protocol = protocol
        self.name = ""
        self.port = ""
        self.description = ""
    def show(self) :
        print "%s:%s:%s:%s" % (
            self.name, self.port, self.protocol, self.description)
    def __repr__(self) :
        return ("%s:%s:%s:%s" % (
                self.name, self.port, self.protocol, self.description)).encode('utf-8')
    def set_name(self, name) :
        self.name = name
    def set_port(self, port) :
        self.port = port
    def set_description(self, description) :
        self.description = description

class NAT_RULE :
    def __init__(self) :
        self.description = ""
    def show(self) :
        print "%s:%s:%s:%s:%s:%s:%s:%s:%s" % (
            self.sequence, self.type, self.nic, self.private_addr,
            self.public_addr, self.protocol, self.port,
            self.status, self.description)
    def __repr__(self) :
        return ("%s:%s:%s:%s:%s:%s:%s:%s:%s" % (
            self.sequence, self.type, self.nic, self.private_addr,
            self.public_addr, self.protocol, self.port,
            self.status, self.description)).encode('utf-8')
    def set_sequence(self, sequence) :
        self.sequence = sequence
    def set_type(self, type) :
        self.type = type
    def set_nic(self, nic) :
        self.nic = nic
    def set_private_addr(self, private_addr) :
        self.private_addr = private_addr
    def set_public_addr(self, public_addr) :
        self.public_addr = public_addr
    def set_protocol(self, protocol) :
        self.protocol = protocol
    def set_port(self, port) :
        self.port = port
    def set_status(self, status) :
        self.status = status
    def set_description(self, description) :
        self.description = description

class Route :
    def __init__(self) :
        pass
    def process_columns(self, route_table_columns) :
        self.set_address(route_table_columns[0])
        self.set_netmask(route_table_columns[1])
        self.set_gateway(route_table_columns[2])
        self.set_interface(route_table_columns[3])
    def set_address(self, addr) :
        self.address = addr
    def set_netmask(self, netmask) :
        self.netmask = netmask
    def set_gateway(self, gateway) :
        self.gateway = gateway
    def set_interface(self, interface) :
        self.interface = interface
    def __repr__(self) :
        return ("%s/%s -> %s : %s" % (
                self.address, self.netmask, 
                self.gateway, self.interface)).encode('utf-8')

class MF2_Config :
    def __init__(self) :
        self.host_objects = []
        self.network_objects = []
        self.group_objects = []
        self.service_objects = []
        self.service_group_objects = []
        self.fw4_rules = []

    def lookup_host_object(self, host_obj_name) :
        for host_obj in self.host_objects :
            if host_obj.name == host_obj_name :
                return host_obj
        return None

    def lookup_network_object(self, network_obj_name) :
        for network_obj in self.network_objects :
            if network_obj.name == network_obj_name :
                return network_obj
        return None

    def lookup_group_object(self, group_obj_name) :
        for group_obj in self.group_objects :
            if group_obj.name == group_obj_name :
                return group_obj
        return None

    def lookup_service_object(self, service_obj_name) :
        for service_obj in self.service_objects :
            if service_obj.name == service_obj_name :
                return service_obj
        return None

    def lookup_service_group_object(self, service_group_obj_name) :
        for service_group_obj in self.service_group_objects :
            if service_group_obj.name == service_group_obj_name :
                return service_group_obj
        return None

    def lookup_host_object_with_addr(self, addr) :
        for host_obj in self.host_objects :
            if host_obj.addr == addr :
                return host_obj
        return None

    def lookup_host_object_with_addr_zone(self, addr, zone) :
        for host_obj in self.host_objects :
            if host_obj.addr == addr and host_obj.zone == zone :
                return host_obj
        return None

    def lookup_network_object_with_addr(self, addr) :
        for nw_obj in self.network_objects :
            if nw_obj.addr == addr :
                return nw_obj
        return None

    def lookup_network_object_with_addr_zone(self, addr, zone) :
        for nw_obj in self.network_objects :
            if nw_obj.addr == addr and nw_obj.zone == zone:
                return nw_obj
        return None

    def lookup_service_object_with_port(self, protocol, direction, port) :
        for service_obj in self.service_objects :
            #print "service_obj.protocol = %s" % service_obj.protocol
            #print "protocol = %s" % protocol
            if service_obj.protocol == protocol :
                if direction == 'SRC' :
                    if service_obj.src_port == port :
                        return service_obj
                elif direction == 'DST' :
                    if service_obj.dst_port == port :
                        return service_obj
                else :
                    print "Direction = %s" % direction
                    return service_obj
        return None

    def add_host(self, host_obj) :
        self.host_objects.append(host_obj)

    def add_network(self, nw_obj) :
        self.network_objects.append(nw_obj)
                
    def add_group(self, group_obj) :
        self.group_objects.append(group_obj)
    
    def add_service(self, service_obj) :
        self.service_objects.append(service_obj)

    def add_service_group(self, service_group_obj) :
        self.service_group_objects.append(service_group_obj)

    def add_fw4_rule(self, fw4_rule) :
        self.fw4_rules.append(fw4_rule)

    def show_network_objects(self) :
        print "Host Objects"
        for host in self.host_objects :
            print host
        print
        print "Network Objects"
        for network in self.network_objects :
            print network
        print
        print "Group Objects"
        for group in self.group_objects :
            print group
        print

    def show_service_objects(self) :
        print "Service Objects"
        for service in self.service_objects :
            print service
        print
        print "Service Group Objects"
        for service_group in self.service_group_objects :
            print service_group
        print

    def show_fw4_rules(self) :
        print "FW4 Rules"
        for rule in self.fw4_rules :
            print rule
        print

    def run_mf2_gui_command(self, cmd) :
        proc = subprocess.Popen([ "cli", "-g", "-c", "conf t", "-c", cmd ],
                                stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        (out_data, err_data) = proc.communicate()
        if err_data != "" :
            print "Command = ", cmd
            print "Error  = ", err_data
            sys.exit(1)

        out_data_list = out_data.split('\n')
        if out_data_list[0] != "OK" :
            code_detail = out_data_list[1].split('^')
            if len(code_detail) < 2 :
                return '-1', out_data
            else:
                return code_detail[0], code_detail[1]
        return '0', "OK"


    # object host NAME zone ZONE ip ADDR description DESCRIPTION
    # object host |^NAME^ZONE^ADDR^DESCRIPTION^F
    def enter_host_object_configs(self) :
        print "Start to generate host objects"
        total = len(self.host_objects)
        for seq, host in enumerate(self.host_objects) :
            cmd_list = [ "object", "host", escape_space(host.name),
                         "zone", host.zone,
                         "ip", host.addr,
                         "description", escape_space(host.description) ] 
            cmd = " ".join(cmd_list)
            res, msg = self.run_mf2_gui_command(cmd)
            if res == '430065' :
                spec_list = [ '|', escape_space(host.name), host.zone, 
                              host.addr, escape_space(host.description), 'F' ]
                cmd = "object host %s" % "^".join(spec_list)
                res, msg = self.run_mf2_gui_command(cmd)
            if res != '0' :
                print "Error <%s> returned from <%s>" % (msg, cmd)
                sys.exit(1)
            update_progress("Host Object: ", total, seq)
        print

    # object network NAME zone ZONE ip (network|range) ADDR/MASK description DESCRIPTION
    def enter_network_object_configs(self) :
        print "Start to generate network objects"
        total = len(self.network_objects)
        for seq, network in enumerate(self.network_objects) :
            if network.addr.find('~') == -1 :
                cmd = ("object network %s zone %s ip network %s description %s"
                       % (escape_space(network.name), network.zone,
                          network.addr, 
                          escape_space(network.description)))
            else :
                cmd = ("object network %s zone %s ip range %s description %s"
                       % (escape_space(network.name), network.zone,
                          network.addr.replace("~", " "), 
                          escape_space(network.description)))
            res, msg = self.run_mf2_gui_command(cmd)
            if res == '430081' or res == '430078' :
                if network.addr.find('~') == -1 :
                    # object network C^|^MMM^internal^10.21.0.0^16^mmm^F
                    base_mask = network.addr.split('/')
                    spec_list = [ 'C', '|', escape_space(network.name), network.zone,
                                  base_mask[0], base_mask[1],
                                  escape_space(network.description), 'F' ]
                else :
                    # object network R^|^TTT^internal^23.43.0.0^23.43.255.255^TTT^F
                    from_to = network.addr.split('~')
                    spec_list = [ 'R', '|', escape_space(network.name), network.zone,
                                  from_to[0], from_to[1],
                                  escape_space(network.description), 'F' ]
                cmd = 'object network %s' % '^'.join(spec_list)
                res, msg = self.run_mf2_gui_command(cmd)
            if res != '0' :
                print "Error <%s> returned from <%s>" % (msg, cmd)
                sys.exit(1)
            update_progress("Network Object: ", total, seq)
        print

    def enter_group_object_configs(self) :
        print 'Start to generate group objects'
        total = len(self.group_objects)
        for seq, network_group in enumerate(self.group_objects) :
            temp_file = tempfile.NamedTemporaryFile(prefix = 'group_child_', 
                                                    dir = '/tmp/', delete = False)
            for member in network_group.member :
                #print escape_space(member)
                print >> temp_file, member.encode('utf-8')
            temp_file.close()
            
            cmd = ("object group %s zone %s child %s description %s"
                   % (escape_space(network_group.name), network_group.zone,
                      temp_file.name, escape_space(network_group.description)))

            res, msg = self.run_mf2_gui_command(cmd)
            if res == '430041' :
                # object group |^VVV^internal^/tmp/group_child_3113547543568871.tmp^|^F
                spec_list = [ '|', escape_space(network_group.name), network_group.zone, 
                              temp_file.name, escape_space(network_group.description), 'F' ]
                cmd = "object group %s" % "^".join(spec_list)
                res, msg = self.run_mf2_gui_command(cmd)
            if res != '0' :
                print 'Error <%s> returned from <%s>' % (msg, cmd)
                sys.exit(1)
            os.unlink(temp_file.name)
            update_progress("Group Object: ", total, seq)
        print

    # object service NAME service-type SVC_TYPE protocol PROTOCOL source-port SRC_PORT \
    #        service-port SVC_PORT lifetime LIFETIME priority PRIORITY sysflag ?? description DESCRIPTION
    def enter_service_object_configs(self) :
        print 'Start to generate service objects'
        total = len(self.service_objects)
        for seq, service in enumerate(self.service_objects) :
            cmd = ("object service %s service-type %s protocol %s source-port %s service-port %s lifetime %s priority %d sysflag N description %s"
                   % (escape_space(service.name), service.svc_type, service.protocol, 
                      service.src_port, service.dst_port, service.life_time, 
                      seq, escape_space(service.description)))
            res, msg = self.run_mf2_gui_command(cmd)
            if res == '430105' and msg.startswith('duplicate contents') :
                # object service |^fff^NONE^TCP^*^114^1800^1^N^|^F
                spec_list = [ '|', escape_space(service.name), service.svc_type,
                              service.protocol, service.src_port, service.dst_port,
                              "%d" % service.life_time, "%d" % seq, "N", 
                              escape_space(service.description), 'F' ]
                cmd = 'object service %s' % '^'.join(spec_list)
                res, msg = self.run_mf2_gui_command(cmd)
            elif res == '430105' and msg.startswith('duplicate entry') :
                print 'Error <%s> returned from <%s>' % (msg, cmd)
                print "Duplicated entry for service: Just Skip"
                res = '0'
            elif res == '430105' and msg.startswith('pool full') :
                print 'Error <%s> returned from <%s>' % (msg, cmd)
                print "too many ports for service: Just Skip"
                res = '0'
            if res != '0' :
                print 'Error <%s> returned from <%s>' % (msg, cmd)
                sys.exit(1)
            update_progress("Service Object: ", total, seq)
        print

    def enter_service_group_object_configs(self) :
        print 'Start to generate service-group objects'
        total = len(self.service_group_objects)
        for seq, service_group in enumerate(self.service_group_objects) :
            temp_file = tempfile.NamedTemporaryFile(prefix = 'service_group_child_', dir = '/tmp/', delete = False)
            for member in service_group.member  :
                print >> temp_file, "SS %s" % escape_space(member)
            temp_file.close()
            
            cmd = ("object service-group %s child %s description %s"
                   % (escape_space(service_group.name), temp_file.name,
                      escape_space(service_group.description)))
            res, msg = self.run_mf2_gui_command(cmd)
            if res == '430107' :
                # object sgroup |^bbb^/secui/gui/MF2Tomcat/gui_temp/service_group_child_3165569169784269.tmp^|^F
                spec_list = [ '|', escape_space(service_group.name), temp_file.name,
                              escape_space(service_group.description), 'F' ]
                cmd = 'object sgroup %s' % '^'.join(spec_list)
                res, msg = self.run_mf2_gui_command(cmd)
            if res != '0' :
                print 'Error <%s> returned from <%s>' % (msg, cmd)
                sys.exit(1)
            os.unlink(temp_file.name)
            update_progress("Service Group Object: ", total, seq)
        print


    # firewall rule Y^1^^IH|10.240.150.131_I^EN|10.140.39.0-41.255_E^SS|SQLNET_1521^allow^ANY^N|NONE^NONE^NONE^NONE^Y^N^default^NONE
    def enter_fw4_rule_configs(self) :
        total = len(self.fw4_rules)
        print 'Start to generate IPv4 firewall rules'
        for seq, rule in enumerate(self.fw4_rules) :
            rule_spec_list = [ "Y" if rule.use else "N", "%d" % (seq + 1), "", 
                               escape_space(",".join(rule.source)), 
                               escape_space(",".join(rule.destination)), 
                               escape_space(",".join(rule.service)),
                               rule.action, "ANY", "N|NONE", "NONE", "NONE", "NONE",
                               "Y" if rule.log else "N", "N", "default", 
                               escape_space(rule.description) ]
            cmd = "firewall rule %s" % "^".join(rule_spec_list)
            res, msg = self.run_mf2_gui_command(cmd)
            if res != '0' :
                print 'Error <%s> returned from <%s>' % (msg, cmd)
                sys.exit(1)
            update_progress("Firewall Rule : ", total, seq)
        print

    def enter_configs(self) :
        self.run_mf2_gui_command("object cancel")
        self.run_mf2_gui_command('firewall cancel')

        self.enter_host_object_configs()
        self.enter_network_object_configs()
        self.enter_group_object_configs()
        
        self.enter_service_object_configs()
        self.enter_service_group_object_configs()

        #self.run_mf2_gui_command("object apply")

        self.enter_fw4_rule_configs()

    def apply_configs(self) :
        pass

class MF2_Host_Object :
    def __init__(self, name, zone, addr, description) :
        self.name = name
        self.zone = zone
        self.addr = addr
        self.description = description
    def __repr__(self) :
        return ("\t%s^%s^%s^%s" % (self.name, self.zone, self.addr, 
                                   self.description)).encode('utf-8')

class MF2_Network_Object :
    def __init__(self, name, zone, addr, description) :
        self.name = name
        self.zone = zone
        self.addr = addr
        self.description = description
    def __repr__(self) :
        return ("\t%s^%s^%s^%s" % (self.name, self.zone, self.addr, 
                                   self.description)).encode('utf-8')

class MF2_Group_Object :
    def __init__(self, name, zone, member, description) :
        self.name = name
        self.zone = zone
        self.member = member
        self.description = description
    def __repr__(self) :
        return ("\t%s^%s^%d^%s" % (self.name, self.zone, len(self.member), 
                                   self.description)).encode('utf-8')

class MF2_Service_Object :
    def __init__(self, name, protocol, svc_type, src_port, dst_port, life_time, description) :
        self.name = name
        self.protocol = protocol
        self.svc_type = svc_type
        self.src_port = src_port
        self.dst_port = dst_port
        self.life_time = life_time
        self.description = description
    def __repr__(self) :
        return ("\t%s^%s^%s^%s^%s^%s^%s" % (self.name, self.protocol, self.svc_type,
                                            self.src_port, self.dst_port, self.life_time,
                                            self.description)).encode('utf-8')

class MF2_Service_Group_Object :
    def __init__(self, name, member, description) :
        self.name = name
        self.member = member
        self.description = description

    def __repr__(self) :
        return ("\t%s^%s^%s" % (self.name, 
                                ",".join([ member.name for member in self.member ]), 
                                self.description)).encode('utf-8')

class MF2_FW4_Rule :
    def __init__(self, use, source, destination, service, action, log, description) :
        self.use = use
        self.source = source
        self.destination = destination
        self.service = service
        self.action = action
        self.log = log
        self.description = description

    def __repr__(self) :
        src_list = []
        source = ','.join(self.source)
        destination = ','.join(self.destination)
        service = ','.join(self.service)
        return ("\t%s^%s^%s^%s^%s^%s" % (self.use, source, destination, service,
                                         self.action, self.log)).encode('utf-8')

h = HTMLParser.HTMLParser()

# 
def get_img_src_path_base(from_where) :
    src_attr = from_where.img['src']
    urlparsed_src_attr = urlparse.urlparse(src_attr)
    path_part_src_attr = urlparsed_src_attr.path
    base_part_src_attr = os.path.basename(path_part_src_attr)
    return base_part_src_attr

def strip_nbsp(t) :
    return t.replace('&nbsp;', ' ').strip()

def remove_extra_space(t) :
    return ' '.join(t.split())

# load data into memory
def read_whole_file(file_name) :
    f = open(file_name)
    r = f.read()
    f.close()
    return r

def get_html_part(data) :
    r = []
    i = 0
    msg = email.message_from_string(data)
    for part in msg.walk() :
        if part.get_content_type() == 'application/octet-stream' :
            # dbg_out(">>>>>>> %d" % i)
            i += 1
            r.append(part.get_payload())
    return r

def extract_main_part(config, filename) :
    global version
    r = ''
    data = read_whole_file(filename)
    html_docs = get_html_part(data)

    if len(html_docs) == 1 :
        # version 2.0
        version = 2.0
        soup = BeautifulSoup(quopri.decodestring(html_docs[0]))
        for child in soup.table.tbody.table.tbody:
            r += child.__str__()
        r = '<html><body>' + r + '</body></html>'
        newsoup = BeautifulSoup(r)
        child_list = []
        for child in newsoup.body:
            if child.__str__().strip() != '' :
                child_list.append(child)
        return child_list
    elif len(html_docs) == 4 :
        # version 3.0 or 4.0
        soup = BeautifulSoup(quopri.decodestring(html_docs[2]).decode('cp949'))
        #soup = BeautifulSoup(html_docs[2])
        allTable = soup.findAll("table")
        if "V3.0" in allTable[-1].__str__() :
            version = 3.0
            if filename == 'nat.mht' :
                tpos = 2
            else :
                tpos = -6
        elif "V4.0" in allTable[-1].__str__() :
            version = 4.0
            if filename == 'network.mht' :
                tpos = 1
            elif filename == 'rule.mht' :
                tpos = 2
                index = 0
                for table in allTable :
                    if table.__str__().find("javascript") != -1:
                        tpos = index + 2
                        break
                    index = index + 1
            elif filename == 'nat.mht' :
                tpos = 2
            elif filename == 'service.mht' :
                tpos = 1
            elif filename == 'routing.mht' :
                tpos = 1
            elif (filename == 'network group.mht' or
                  filename == 'network_group.mht') :
                tpos = 1
            else :
                tpos = -6
        else :
            dbg_out("Unknown Version")
            sys.exit(-1)
        for child in allTable[tpos].tbody.table.tbody:
            r += child.__str__()
        r = '<html><body>' + r + '</body></html>'
        newsoup = BeautifulSoup(r)
        child_list = []
        for child in newsoup.body:
            if child.__str__().strip() != '' :
                child_list.append(child)
        #print child_list[0]
        #print child_list[1]
        #print len(child_list)
        return child_list

def extract_main_part_new(filename) :
    r = ''
    data = read_whole_file(filename)
    html_doc = get_html_part(data)
    soup = BeautifulSoup(quopri.decodestring(html_doc))
    for child in soup.table.tbody:
        r += child.__str__()
    r = '<html><body>' + r + '</body></html>'
    newsoup = BeautifulSoup(r)
    child_list = []
    for child in newsoup.body:
        if child.__str__().strip() != '' :
            child_list.append(child)
    return child_list

def process_route(config, route_list) :
    #for route in route_list :
    #    print route
    route_table = []
    route_table_columns = []
    if version == 2.0 :
        t = route_list[0]
    else :
        t = route_list[1]
    for col in t :
        if col.__str__().strip() != '':
            route_table_columns.append(col.b.contents[0])
    route = Route()
    route.process_columns(route_table_columns)
    route_table.append(route)        
    for route_entry in route_list[2:] :
        route_table_columns = []
        for col in route_entry :
            if col.__str__().strip() != '':
                route_table_columns.append(h.unescape(col.contents[0]).strip())
        route = Route()
        route.process_columns(route_table_columns)
        route_table.append(route)
    config['Routing Table'] = route_table            
    pass

def process_network_name(interface, name) :
    if version == 2.0 :
        t = name.contents[0]
    else :
        if name.b != None :
            t = h.unescape(name.b.a.contents[0]).strip()
        else :
            t = h.unescape(name.contents[0]).strip()
    interface.set_name(t)

def process_network_address(interface, address) :
    addr_list = []
    for addr in address :
        t = h.unescape(addr)
        if t.strip != None :
            t = t.strip()
            if t != '' :
                addr_list.append(t)
    interface.set_addr(addr_list)

def process_network_hw_addr(interface, hw_addr) :
    interface.set_hw_addr(hw_addr.contents[0])

def process_network_mtu(interface, mtu) :
    interface.set_mtu(mtu.contents[0])

def process_network_status(interface, status) :
    interface.set_status(status.contents[0])

def process_network_zone(interface, zone) :
    if zone.select :
        for sel in zone.select :
            if sel.__str__().strip() != '' :
                if len(sel.attrs) == 2 :
                    interface.set_zone(sel.contents[0])

def process_network_description(interface, description) :
    t = h.unescape(description.contents[0]).strip()
    # dbg_out(t)
    interface.set_description(t)

def process_network_level(interface, level) :
    interface.set_level(h.unescape(level.contents[0]).strip())

def process_network_type(interface, addr_type) :
    if addr_type.img != None :
        icon = os.path.basename(
            urlparse.urlparse(addr_type.img.attrs[1][1]).path)
        if icon == 'i_host.gif' :
            interface.set_type('Host')
        elif icon == 'i_network.gif' :
            interface.set_type('Network')
        elif icon == 'i_mhost.gif' :
            interface.set_type('Host Group')
        else :
            dbg_out('Invalid Interface Address Type: %s' % icon)

def process_network(config, network_list) :
    #print "Number of network = ", len(network_list) - 4
    # parsing header
    interface_list = []
    network_columns = []
    for col in network_list[0] :
        if col.__str__().strip() != '':
            if version == 2.0 :
                t = col.font.contents[0].strip()
            else :
                t = col.contents[0].strip()
            network_columns.append(t)
    interface = Interface()
    if version == 2.0 :
        interface.set_name(network_columns[0])
        interface.set_addr(network_columns[1])
        interface.set_hw_addr(network_columns[2])
        interface.set_mtu(network_columns[3])
        interface.set_status(network_columns[4])
        interface.set_zone(network_columns[5])
    else :
        interface.set_name(network_columns[0])
        interface.set_description(network_columns[1])
        interface.set_zone(network_columns[2])
        interface.set_level(network_columns[3])
        interface.set_type(network_columns[4])
        interface.set_addr(network_columns[5])
    # interface.show()
    interface_list.append(interface)
    if version == 2.0 :
        range_end = -2
    else :
        range_end = None
    for network in network_list[2:range_end] :
        #print network[0]
        network_columns = []
        for col in network :
            if col.__str__().strip() != '':
                network_columns.append(col)
        interface = Interface()
        if version == 2.0 :
            process_network_name(interface, network_columns[0])
            process_network_address(interface, network_columns[1])
            process_network_hw_addr(interface, network_columns[2])
            process_network_mtu(interface, network_columns[3])
            process_network_status(interface, network_columns[4])
            process_network_zone(interface, network_columns[5])
        else :
            process_network_name(interface, network_columns[0])
            process_network_description(interface, network_columns[1])
            process_network_zone(interface, network_columns[2])
            process_network_level(interface, network_columns[3])
            process_network_type(interface, network_columns[4])
            process_network_address(interface, network_columns[5])
            
        # interface.show()
        interface_list.append(interface)
    config['Network Interfaces'] = interface_list
    pass

def process_network_group_name(n_object, name) :
    if name.a != None :
        n_object.set_name(escape_special_for_name(name.a.contents[0].strip()))
    else :
        n_object.set_name(escape_special_for_name(strip_nbsp(name.contents[0].strip())))

def process_network_group_description(n_object, description) :
    #if description.contents[0] != '&nbsp;' :
    #    n_object.set_description(h.unescape(description.contents[0]))
    #else :
    #    n_object.set_description('')
    if description.contents != None :
        t = description.contents[0].strip()
        if t != '&nbsp;' :
            descr = strip_nbsp(remove_extra_space(t))
            if descr == '' :
                descr = ' '
            n_object.set_description(descr)

def process_network_group_zone(n_object, zone) :
    icon = get_img_src_path_base(zone)
    if icon == 'i_trust.gif' :
        n_object.set_zone('internal')
    elif icon == 'i_untrust.gif' :
        n_object.set_zone('external')
    elif icon == 'i_service.gif' :
        n_object.set_zone('dmz')
    else :
        print "Unknown Network Group Zone : %s in %s" % (icon, n_object.name)

def process_network_group_level(n_object, level) :
    if level.contents[0] != '&nbsp;' :
        n_object.set_level(strip_nbsp(level.contents[0].strip()))
    else :
        n_object.set_level('')

def process_network_group_type(n_object, type) :
    if type.img != None :
        icon = get_img_src_path_base(type)
        if icon == 'i_host.gif' :
            n_object.set_type('Host')
        elif icon == 'i_mhost.gif' :
            n_object.set_type('Host Group')
        elif icon == 'i_network.gif' :
            n_object.set_type('Network')
        else :
            dbg_out('Invalid Network Group Type: %s' % icon)
    else :
        n_object.set_type('None')

def process_network_group_addr(n_object, addr) :
    addr_list = []
    for content in addr.contents :
        t = content.__str__()
        if t != '<br />' and t != '&nbsp;' :
            addr_list.append(strip_nbsp(t))
    n_object.set_addr(addr_list)

def process_network_group_etc(n_object, etc) :
    # ignore etc
    pass

def process_service_name(service, name) :
    if name.a != None :
        t = name.a.contents[0].strip()
    else :
        t = name.contents[0].strip()
    
    if t != '&nbsp;' :
        service.set_name(escape_special_for_name(strip_nbsp(remove_extra_space(t))))

def process_service_port(service, port) :
    #print "service_port = %s" % port
    port_list = []
    for content in port.contents :
        t = content.__str__()
        if t != '<br />' and t != '&nbsp;' :
            port_list.append(strip_nbsp(t))
    #print "port_list = %s" % port_list
    service.set_port(port_list)

def process_service_description(service, description) :
    if description.contents != None :
        t = description.contents[0].strip()
        if t != '&nbsp;' :
            descr = strip_nbsp(remove_extra_space(t))
            if descr == '' :
                descr = ' '
            service.set_description(descr)

def process_rule_sequence(rule, sequence) :
    icon = get_img_src_path_base(sequence)
    if version == 2.0 :
        if icon == 'i-allow.gif' :
            rule.set_action('allow')
        elif icon == 'i-deny.gif' :
            rule.set_action('deny')
        else :
            dbg_out("Invalid Rule Action Icon: %s" % icon)
        t = sequence.a.contents[0].encode('utf-8')
    elif version == 3.0 or version == 4.0 :
        if icon == 'i_direction.gif' :
            rule.set_direction("UNI")
        elif icon == 'i_bidirection.gif' :
            rule.set_direction("BI")
        else :
            dbg_out('Invalid Rule Direction Icon: %s' % icon)
        t = sequence.a.nextSibling.nextSibling.nextSibling.contents[0].encode('utf-8')
    
    rule.set_sequence(t)

def process_rule_NIC(rule, nic) :
    t = nic.contents[0].strip()
    rule.set_nic(t)

def process_rule_source(rule, source) :
    if version == 2.0 :
        t = source.a.contents[0]
    elif version == 3.0 :
        t = source.b.contents[0]
    elif version == 4.0 :
        t = [ escape_special_for_name(b.contents[0].strip()) for b in source.findAll('b') ]
    rule.set_source(t)

def process_rule_destination(rule, destination) :
    if version == 2.0 :
        t = destination.a.contents[0]
    elif version == 3.0 :
        t = destination.b.contents[0]
    elif version == 4.0 :
        t = [ escape_special_for_name(b.contents[0].strip()) for b in destination.findAll('b') ]
    rule.set_destination(t)

def process_rule_service(rule, service) :
    if version == 3.0 or version == 4.0:
        service_list = [ escape_special_for_name(remove_extra_space(b.contents[0].strip()))
                         for b in service.findAll('b') ]
        rule.set_service(service_list)
    else :
        service_list = []
        new_serv = ''
        for serv in service.contents :
            t = h.unescape(serv.__str__()).strip()
            if t.startswith('<b>') and t.endswith('</b>') :
                t = " ".join([ s.strip() for s in t[3:-4].splitlines() ])
            if t != '<br />' :
                new_serv += t
            else :
                service_list.append(new_serv)
                new_serv = ''
        if new_serv != '' :
            service_list.append(new_serv)
        rule.set_service(service_list)

def process_rule_user(rule, user) :
    # ignore user in rule
    pass

def process_rule_timerange(rule, timerange) :
    # ignore time range in rule
    pass

def process_rule_etc(rule, etc) :
    # Only handle log status
    icon = get_img_src_path_base(etc)
    if icon == 'logok.gif' :
        rule.set_log('Enable')
    else :
        rule.set_log('Disable')

def process_rule_description(rule, description) :
    # ignore description in rule
    if version == 2.0 :
        t = description.contents[0]
    elif version == 3.0 :
        t = description.contents[4][2:]
    elif version == 4.0 :
        t = remove_extra_space(description.contents[-1])
        print t
    rule.set_description(t)

def process_rule_action(rule, action) :
    icon = get_img_src_path_base(action)
    if icon == 'i_qos-allow-4.gif' :
        t = "allow"
    elif icon == 'i_qos-deny.gif':
        t = "deny"
    elif icon == 'i_qos-crypt-4.gif' :
        t = "VPN"
    else :
        dbg_out('Unknown icon for rule action : %s' % icon)
    rule.set_action(t)

def process_rule_log(rule, log) :
    icon = get_img_src_path_base(log)
    # dbg_out(icon)
    if icon == 'i_log.gif' :
        rule.set_log('Enable')
    else :
        rule.set_log('Disable')

def process_rule_status(rule, status) :
    icon = get_img_src_path_base(status)
    if version == 2.0 :
        if icon == 'disable.gif' :
            rule.set_status('Disable')
        elif icon == 'enable.gif' :
            rule.set_status('Enable')
        else :
            dbg_out("Invalid Rule Status : %s" % icon)
    else:        
        if icon == 'i_on.gif' :
            rule.set_status('Enable')
        elif icon == 'i_off.gif' :
            rule.set_status('Disable')
        else :
            dbg_out("Invalid Rule Status : %s" % icon)

def process_rule_entry(rule_columns) :
    rule = Rule()
    if version == 2.0 :
        process_rule_sequence(rule, rule_columns[0])
        process_rule_NIC(rule, rule_columns[1])
        process_rule_source(rule, rule_columns[2])
        process_rule_destination(rule, rule_columns[3])
        process_rule_service(rule, rule_columns[4])
        process_rule_user(rule, rule_columns[5])
        process_rule_timerange(rule, rule_columns[6])
        process_rule_status(rule, rule_columns[7])
        process_rule_etc(rule, rule_columns[8])
        process_rule_description(rule, rule_columns[9])
    elif version == 3.0 :
        process_rule_sequence(rule, rule_columns[0])
        process_rule_NIC(rule, rule_columns[1])
        process_rule_source(rule, rule_columns[2])
        process_rule_destination(rule, rule_columns[3])
        process_rule_service(rule, rule_columns[4])
        process_rule_user(rule, rule_columns[5])
        process_rule_timerange(rule, rule_columns[6])
        process_rule_action(rule, rule_columns[7])
        process_rule_log(rule, rule_columns[9])
        process_rule_status(rule, rule_columns[10])
    elif version == 4.0 :
        process_rule_sequence(rule, rule_columns[0])
        process_rule_NIC(rule, rule_columns[1])
        process_rule_source(rule, rule_columns[2])
        process_rule_destination(rule, rule_columns[3])
        process_rule_service(rule, rule_columns[4])
        process_rule_user(rule, rule_columns[5])
        process_rule_timerange(rule, rule_columns[6])
        process_rule_action(rule, rule_columns[7])
        process_rule_log(rule, rule_columns[11])
        process_rule_status(rule, rule_columns[13])
    return rule

def process_nat_sequence(nat_rule, sequence) :
    #print sequence.contents[0]
    if version == 2.0 :
        t = sequence.a.contents[0]
    else :
        t = sequence.contents[0]
    nat_rule.set_sequence(t)
    pass

def process_nat_type(nat_rule, type) :
    t = get_img_src_path_base(type)
    #if version == 2.0 :
    #    t = os.path.basename(urlparse.urlparse(
    #            type.img.attrs[0][1]).path)
    #elif version == 3.0 :
    #    t = os.path.basename(urlparse.urlparse(
    #            type.img.attrs[0][1]).path)
    #elif version == 4.0 :
    #    t = os.path.basename(urlparse.urlparse(
    #            type.img.attrs[1][1]).path)

    nat_rule.set_type(t)
    pass

def process_nat_nic(nat_rule, nic) :
    nat_rule.set_nic(strip_nbsp(nic.contents[0].strip()))
    pass

def process_nat_private_addr(nat_rule, private_addr) :
    if version == 2.0 :
        t = private_addr.a.contents[0].strip()
    else :
        t = private_addr.b.contents[0].strip()
    nat_rule.set_private_addr(t)
    pass

def process_nat_public_addr(nat_rule, public_addr) :
    if version == 2.0 :
        t = public_addr.a.contents[0].strip()
    else :
        t = public_addr.b.contents[0].strip()
    nat_rule.set_public_addr(t)
    pass

def process_nat_protocol(nat_rule, protocol) :
    #print protocol.contents[0]
    nat_rule.set_protocol(protocol.contents[0])
    pass

def process_nat_port(nat_rule, port) :
    #print port.contents[0]
    nat_rule.set_port(port.contents[0])
    pass

def process_nat_status(nat_rule, status) :
    t = os.path.basename(
        urlparse.urlparse(status.img.attrs[0][1]).path)
    nat_rule.set_status(t)
    pass

def process_nat_etc(nat_rule, etc) :
    # ignore
    # print etc 
    pass

def process_nat_description(nat_rule, description) :
    if version == 2.0 : 
        nat_rule.set_description(description.contents[0])
    elif version == 3.0 :
        nat_rule.set_description(description.contents[4][2:])
    else :
        print description.contents
        nat_rule.set_description(description.contents[0])
    pass

def make_oulim_config() :
    # gather information from Oulim's configuration file
    config = Config()

    mht_list = [ fn for fn in os.listdir('.') if fnmatch.fnmatch(fn, "*.mht") ]
    for filename in os.listdir('.') :
        if not fnmatch.fnmatch(filename, "*.mht") :
            continue
        dbg_out("Processing %s" % filename)
        if filename == 'routing.mht' :
            soup = extract_main_part(config, filename)
            if soup != None :
                config.process_route(soup)
        elif filename == 'network.mht' :
            soup = extract_main_part(config, filename)
            if soup != None :
                config.process_network(soup)
        elif (filename == 'network group.mht' 
              or filename == 'network_group.mht') :
            soup = extract_main_part(config, filename)
            if soup != None :
                config.process_network_group(soup)
        elif filename == 'servicetcp.mht' :
            soup = extract_main_part(config, filename)
            if soup != None :
                config.process_service(soup, 'TCP')
        elif filename == 'serviceudp.mht' :
            soup = extract_main_part(config, filename)
            if soup != None :
                config.process_service(soup, 'UDP')
        elif filename == 'service.mht' :
            soup = extract_main_part(config, filename)
            if soup != None :
                config.process_service(soup)
        elif filename == 'rule.mht' :
            soup = extract_main_part(config, filename)
            if soup != None :
                config.process_rule(soup)
        elif filename == 'nat.mht' :
            soup = extract_main_part(config, filename)
            if soup != None :
                config.process_nat(soup)
        else :
            dbg_out('Unhandle config %s ' % filename)
    config.show()
    return config

if __name__ == '__main__' :
    parser = optparse.OptionParser(
        description = 'Convert Oulim\'s config to Secui MF2\'s config.')
    parser.add_option('-i', '--interact', action = 'store_true', 
                      help = 'interactive mode')
    parser.add_option('-c', '--clear', action = 'store_true',
                      help = 'clear the cached oulim config')
    (options, args) = parser.parse_args()

    if not options.clear and os.access('Oulim.pickle', os.F_OK) :
        with file('Oulim.pickle') as pkl_file:
            config = pickle.load(pkl_file)
    else:
        config = make_oulim_config()
        with file('Oulim.pickle', 'w') as pkl_file:
            pickle.dump(config, pkl_file)

    config.verify_rule()
    mf2_config = MF2_Config()
    config.register_mf2_config(mf2_config)
    #config.set_output_file('gen_mf2_rule.sh')
    config.translate_network_object()
    #mf2_config.show_network_objects()
    config.translate_service_object()
    #mf2_config.show_service_objects()
    config.translate_fw4_rule()
    #mf2_config.show_fw4_rules()
    #config.close_output_file()
    mf2_config.enter_configs()
    mf2_config.apply_configs()

    if options.interact :
        print "Start a cmd processor."
        cmd_processor = cmd.Cmd()
        cmd_processor.cmdloop()
